package service

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"path"
	"sync"
	"syscall"
	"time"

	"gitee.com/zacyuan/yuan/pkg/config"
	"gitee.com/zacyuan/yuan/pkg/log"
	"gitee.com/zacyuan/yuan/pkg/server"
)

type Start func() error

type Service struct {
	once    sync.Once
	starts  []Start
	servers []server.Server
	quit    chan os.Signal
}

func New() *Service {
	return &Service{
		starts: []Start{
			config.Start,
			log.Start,
		},
		quit: make(chan os.Signal, 1),
	}
}

func (s *Service) Init(starts ...Start) *Service {
	if len(starts) > 0 {
		s.starts = append(s.starts, starts...)
	}
	return s
}

func (s *Service) Servers(servers ...server.Server) *Service {
	if len(servers) > 0 {
		s.servers = append(s.servers, servers...)
	}
	return s
}

func (s *Service) Start() {
	pid := path.Base(os.Args[0]) + ".pid"
	_, err := os.Stat(pid)
	if err == nil {
		log.Fatal("server is running...")
	}

	str := fmt.Sprintf("%d", os.Getpid())
	err = os.WriteFile(pid, []byte(str), os.FileMode(0644))
	if err != nil {
		log.Fatal(err)
	}

	defer os.Remove(pid)

	s.once.Do(func() {
		signal.Notify(s.quit, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)

		for _, start := range s.starts {
			err := start()
			if err != nil {
				log.Error(err)
				return
			}
		}

		ctx := context.Background()
		for _, svr := range s.servers {
			err := svr.Start(ctx)
			if err != nil {
				log.Error(err)
				return
			}
		}

		<-s.quit

		stopCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
		defer cancel()
		for _, svr := range s.servers {
			err := svr.Stop(stopCtx)
			if err != nil {
				log.Error(err)
				return
			}
		}
	})
}
